到目前為止,我們已經在在install階段中實現了pre-cache靜態資源,現在讓我們也來實作Dynamic Caching。首先動態暫存意味著仍然會發出一個fetch event,我們只需要將返回的response暫存到cache中。所以要做到這一點,我們必須要修改一下sw.js中fetch event lister的回傳結果。
回顧一下,我們目前的策略是會先到cache中尋找是否有我們「預先暫存好」的資源,若有(也就是response不為null)則直接回傳cache中的值,沒有的話則必須要透過fetch API取得該資源後再回傳。
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
if(response) {
return response;
} else {
return fetch(event.request);
}
});
現在應該要改成當沒有在cache中的話,一樣透過fetch API去請求外部資源,只不過將回傳的結果再做進一步的處理。我先再創建一個sub-cache名叫「dynamic」(說明是要暫存動態資源的部分),然後用cache.put()將「request (key)」和「回傳的response (value)」暫存到dynamic sub-cache中。
來看一下要怎寫吧:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
if(response) {
return response;
} else {
return fetch(event.request).then(function(res) {
return caches.open('dynamic').then(function(cache) {
cache.put(event.request.url, res.clone());
return res;
})
}).catch(function(err) {
});
}
})
);
});
這裡有幾點要特別明:
讓我們來看一下如果我今天修改feed.js中的createCard()將title顏色設為白色,看一下畫面結果:
cardTitleTextElement.style.color = 'white';
哇!!! 沒有改變 ... WHY??
原因是我們雖然修改了js file,但是service worker並沒有更新,所以還是繼續拿原本static cache中的feed.js來執行。最常見的解決發案是每次修該相關的靜態檔案時,都要更新sub-cache的名稱(也就是增加新的版本號),並把舊版本的sub-cache清除。
那清除舊版本sub-cache的code應該寫在哪?較好的地方是在service worker的activate階段,因為只有當在用戶關閉所有頁面,並打開新的應用程式後才會執行此操作,所以現在更新cache才是安全的(由於用戶已經不在運行的應用程式中了)。
來看要怎麼清除cache吧
var CACHE_STATIC_NAME = 'static-v4';
var CACHE_DYNAMIC_NAME = 'dynamic-v2';
self.addEventListener('activate', function(event) {
console.log('[Service Worker] Activating Service Worker ...', event);
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if(key !== CACHE_STATIC_NAME && key !== CACHE_DYNAMIC_NAME) {
console.log('[Service Worker] Removing old cache.', key);
return caches.delete(key);
}
}));
})
);
return self.clients.claim();
});
說明一下,caches.keys()會回傳一個「sub-cache名稱所形成的字串陣列」,之後我將移除的部分寫在Promise.all()裡面,確保全部執行完清除cache的邏輯後才會回傳。
不過由於Promise.all()裡面必須是promise object,所以我必須將keyList透過js原生的map()函式把「每個key值(也就是sub-cache名稱)」對應到「清除cache的function」來執行我要的判斷。
今天先到這兒囉~~
Day10 結束!!